#!/usr/bin/php
<?php
/*
	Project:	Brutis
	Version:	0.93
	Author:		Zach Younker
	Copyright:

		Software License Agreement (BSD License)

		Copyright (c) 2009, Gear Six, Inc.
		All rights reserved.

		Redistribution and use in source and binary forms, with or without
		modification, are permitted provided that the following conditions are
		met:

		* Redistributions of source code must retain the above copyright
		  notice, this list of conditions and the following disclaimer.

		* Redistributions in binary form must reproduce the above
		  copyright notice, this list of conditions and the following disclaimer
		  in the documentation and/or other materials provided with the
		  distribution.

		* Neither the name of Gear Six, Inc. nor the names of its
		  contributors may be used to endorse or promote products derived from
		  this software without specific prior written permission.

		THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
		"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
		LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
		A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
		OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
		SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
		LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
		DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
		THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
		(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
		OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

*/

/* Set error reporting to ALL */
error_reporting(E_ALL);

require_once('lib/task_manager.php');
require_once('lib/functions.php');

/* Define how to run collector by extending Task class */
class collector extends Task{
	function exec() {
		/* Execute collector */
		parent::exec();
		global $port;

		if ($this->args['verbose']) {
			print("Starting collector\n");
			$options[] = '-v';
		}
		$options[] = '-F' . $this->args['output_format'];

		$options[] = '-i' . $this->args['poll'];

		if ($this->args['filename'] != NULL) {
			$options[] = '-o' . $this->args['filename'];
		}
		$options[] = '-c' . $this->args['collector']['server'] . ":". $this->args['collector']['port'];
		if (isset($options)) {
			/*pcntl_exec('lib/collector', $options);*/
			pcntl_exec('lib/collector', $options);
		} else {
			pcntl_exec('lib/collector', array());
			/*pcntl_exec('lib/collector', array());*/
		}
	}
	function finish() {
		/* Code to run when collector finishes */
		parent::finish();
		if ($this->exit_code > 0) {
			print ("collector exit code " . $this->exit_code . "!\n");
		}
	}
}

/* Define how to run client by extending Task class */
class client extends Task{
	function exec() {
		/* Execute client */
		parent::exec();
		$options[] = '-k' . $this->args['max_keys'];
		if($this->args['prefix']) {
			$options[] = '-p' . $this->args['prefix'];
		}
		if($this->args['persistent']) {
			$options[] = '-P';
		}
		$options[] = '-R' . $this->args['reconnect'];
		$options[] = '-m' . $this->args['multiplier'];
		$options[] = '-r' . $this->args['set_ratio'] . ':' . $this->args['get_ratio'];
		$options[] = '-a' . $this->args['set_pattern'] . ':' . $this->args['get_pattern'];
		$options[] = '-z' . $this->args['offset'];
		if ($this->args['batch'] > 0) {
			$options[] = '-b' . $this->args['batch'];
		}
		if ($this->args['operations'] != NULL) {
			$options[] = '-n' . $this->args['operations'];
		}
		if ($this->args['runtime'] != NULL) {
			$options[] = '-t' . $this->args['runtime'];
		}
		if ($this->args['checksum'] == TRUE) {
			$options[] = '-d';
		}
		$options[] = '-s' . $this->args['object_size'];
		$options[] = '-i' . $this->args['poll'];
		$server_list = NULL;
		for ($i = 0; $i < count($this->args['memcache']); $i++) {
			if ($i != (count($this->args['memcache']) - 1)) {
				$server_list .= $this->args['memcache'][$i]['server'] . ":" .
					$this->args['memcache'][$i]['tcp_port'] . ':' .
					$this->args['memcache'][$i]['udp_port'] . ',';
			} else {
				$server_list .= $this->args['memcache'][$i]['server'] . ":" .
					$this->args['memcache'][$i]['tcp_port'] . ":".
					$this->args['memcache'][$i]['udp_port'];
			}
		}
		$options[] = "-x" . trim($server_list);
                if ($this->args['filename'] != NULL 
                    || $this->args['verbose'] == TRUE
                    || $this->args['use_collector'] == true) {
			$options[]='-c' . $this->args['collector']['server'] . ":" . $this->args['collector']['port'];
                }

		pcntl_exec('lib/client', $options);
	}
	function finish() {
		/* Code to run when collector finishes */
		parent::finish();

		if ($this->exit_code > 0) {
			print ("client pid " . $this->pid . " exit code " . $this->exit_code . "!\n");
		}
	}
}

function init_settings($argc, $argv) {
/*	init_settings()
	Get runtime arguments and convert them to settings.
	@param int $argc number of arguments
	@param mixed $argv,... runtime arguments
*/
	$options = getopt("Pc:Da:z:dk:p:r:n:t:f:s:o:F:x:vhb:i:m:R:");

	if (isset($options['h']) || !isset($options['x'])) {
		print (
			"\nUsage: Brutis -x <server_list> [OPTIONS]\n"
			. "	-c <collector>			Specifiy collector host\n"
			. "	-D       			Disable staring of local collector\n"
			. "	-a <#:#>			Access Pattern (r=random, s=sequential)\n"
			. "	-z <#>				Key id Offset\n"
			. "	-b <#>				Number of gets to batch together\n"
			. "	-i <#>				Collector Poll time in seconds\n"
			. "	-d 				Enable data checksum\n"
			. "	-k <#>	 			Max keys\n"
			. "	-p <prefix> 			key prefix\n"
			. "	-P  				Enable persistent connections\n"
			. "	-m <#>  			ConnectionPool multiplier\n"
			. "	-r <#:#>			Ratio Sets:Gets\n"
			. "	-R <#>				Non-persistent disconnect/reconnect timer\n"
			. "	-n <#>				Total number of operations\n"
			. "	-t <#>				Time in seconds to run tests default(48hours)\n"
			. "	-f <#>				Processes to fork\n"
			. "	-s <#>				Object size in bytes\n"
			. "	-o <filename>			Write results output\n"
			. "	-F <format>			Results output format\n"
			. "	-x <serverlist,>		list of servers (la-vip-1:11211:11211,la-vip-2:11211:11211,..)\n"
			. "	-v 				Verbose\n"
			. "	-h				Help\n"
			. "\n\n");
		exit(1);
	}

	parse_collector($options, 'c');
	parse_disable_collector($options, 'D');
	parse_offset($options, 'z');
	parse_access_pattern($options, 'a');
	parse_keys($options, 'k');
	parse_checksum($options, 'd');
	parse_prefix($options, 'p');
	parse_persistent($options, 'P');
	parse_reconnect($options, 'R');
	parse_multiplier($options, 'm');
	parse_ratio($options, 'r');
	parse_operations($options, 'n');
	parse_runtime($options, 't');
	parse_forks($options, 'f');
	parse_object_size($options, 's');
	parse_output($options, 'o');
	parse_output_format($options, 'F');
	parse_mc_servers($options, 'x');
	parse_verbose($options, 'v');
	parse_batch($options, 'b');
	parse_poll($options, 'i');

}

/* MAIN */
$settings = array();
init_settings($argc, $argv);
check_libs();
$tm = new TaskManager();

/* set maxruntime */
if ($settings['runtime'] !== NULL) {
    $settings['maxruntime'] = $settings['runtime'] + 10;
}

/* If collector specified is this host, add collector to taskmanager to be executed */
if ($settings['disable_collector'] == FALSE) {
	$tm->add_task(new collector(), $settings);
}

/* Add clients to task manager to be executed. */
for ($i = 1; $i <= $settings['forks']; $i++) {
	$tm->add_task(new client(), $settings);
}

/* Execute Tasks */
$tm->run();

?>
